package com.eugenema.service;

import com.eugenema.entity.TestCase;
import com.eugenema.entity.TestStep;
import com.eugenema.entity.TestUnit;
import com.eugenema.util.ConfigUtil;
import com.eugenema.util.SeleniumUtil;
import com.eugenema.util.SeleniumXmlParseUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Method;

/**
 * 运行 Selenium 脚本服务
 * <br/>一个实例，代表一个 TestUnit
 * <br/>解析脚本并提供运行某个步骤或单元的方法
 *
 * @author Eugenema
 * @date 2023/3/9 9:43
 **/
public class RunSeleniumService {
    private static Logger logger = LoggerFactory.getLogger(RunSeleniumService.class);

    /**
     * 执行单元
     */
    private TestUnit testunit;

    /**
     * 构造方法
     * <br/> 初始化浏览器驱动、解析执行脚本、加载所有配置参数
     * <br/> 若浏览器已打开，即浏览器驱动已初始化，则不重复初始化
     * <br/> 配置文件会每次运行均加载，不论浏览器驱动是否已初始化
     *
     * @param xmlFileName 要解析的脚本 xml 文件名，包括扩展名
     * @author Eugenema
     * @date 2023/3/10 22:50
     **/
    public RunSeleniumService(String xmlFileName) {
        testunit = SeleniumXmlParseUtil.parse(xmlFileName);
        // 读取所有配置参数
        for (String configKey : ConfigUtil.ALL_DEMAND_CONFIG) {
            SeleniumXmlParseUtil.parseParameter(ConfigUtil.getProperty(configKey));
        }
    }

    /**
     * 打开浏览器
     *
     * @author 马寅广
     * @date 2024/1/20 20:16
     *
     *
     * @return
     **/
    public void openBrow() {
        if (SeleniumUtil.getDriver() == null) {
            // 必须加载浏览器个人配置
            SeleniumUtil.initBrowserDriver(testunit.getBrowserName(), true, testunit.getNoHeadLess());
        }
    }

    /**
     * 执行整个单元操作
     *
     * @author Eugenema
     * @date 2023/3/11 12:14
     **/
    public void runUnit() {
        //跳过不需要执行的单元
        if (testunit.getCancel()) {
            logger.info("TestUnit【id：{}，desc：{}】已设置跳过，本次不执行", testunit.getId(), testunit.getDesc());
            return;
        }


        //依次执行 case
        logger.info("TestUnit【id：{}，desc：{}】开始执行", testunit.getId(), testunit.getDesc());
        testunit.getCaseSet().stream().filter(testCase -> !testCase.getCancel()).forEach(testCase -> runCase(testCase));
    }

    /**
     * 执行单个 case 步骤
     * <br/>不考虑 case 元素的 cancel 属性
     *
     * @param id case 元素 id
     * @author Eugenema
     * @date 2023/3/11 18:34
     **/
    public void runCase(String id) {
        TestCase testCase = getCase(id);
        if (testCase == null) {
            logger.error("TestCase【id：{}】未找到，执行失败", id);
            return;
        }

        //执行步骤
        runCase(testCase);
    }

    /**
     * 执行单个 step 动作
     *
     * @param id step 元素 id
     * @author 马寅广
     * @date 2023/3/13 11:31
     **/
    public void runStep(String id) {
        TestStep testStep = getStep(id);
        if (testStep == null) {
            logger.error("TestStep【id：{}】未找到，执行失败", id);
            return;
        }

        //执行动作
        runStep(testStep);
    }

    /**
     * 执行动作
     * <br/>每次执行动作后，均等待一段时间
     * <br/>不考虑 step 元素的 cancel 属性
     *
     * @param testStep 动作
     * @author Eugenema
     * @date 2023/3/12 14:17
     **/
    public void runStep(TestStep testStep) {
        try {
            logger.info("TestStep【id：{}，desc：{}】开始执行", testStep.getId(), testStep.getDesc());
            Class<?> clazz = testStep.getAction().getHandler();
            String methodName = testStep.getAction().getKey();
            Method method = clazz.getDeclaredMethod(methodName, new Class<?>[]{TestStep.class});
            method.invoke(clazz.newInstance(), testStep);

            //等待
            Thread.sleep(Long.parseLong(ConfigUtil.getProperty(ConfigUtil.RUNED_WAIT_TIME)));
        } catch (Exception e) {
            throw new RuntimeException(String.format("执行 TestStep【id：%s，desc：%s】失败：", testStep.getId(), testStep.getDesc()), e);
        }
    }

    /**
     * 根据元素 id，获取指定步骤下的指定动作
     *
     * @param id step 元素 id
     * @return step 元素，若未找到，返回 null
     * @author 马寅广
     * @date 2023/3/13 10:14
     **/
    public TestStep getStep(String id) {
        return testunit.getCaseSet()
                .stream()
                .flatMap(testCase -> testCase.getStepSet().stream())
                .filter(testStep -> testStep.getId().equals(id))
                .findFirst()
                .orElse(null);
    }

    /**
     * 执行 case 内的所有动作
     *
     * @param testCase 动作
     * @author Eugenema
     * @date 2023/3/11 18:39
     **/
    private void runCase(TestCase testCase) {
        //依次执行 step
        logger.info("TestCase【id：{}，desc：{}】开始执行", testCase.getId(), testCase.getDesc());
        testCase.getStepSet().stream().filter(testStep -> !testStep.getCancel())
                .forEach(testStep -> runStep(testStep));
    }

    /**
     * 根据 case 元素 id，获取 case 元素
     *
     * @param id case 元素 id
     * @return case 元素，若未找到，返回 null
     * @author Eugenema
     * @date 2023/3/9 9:45
     **/
    private TestCase getCase(String id) {
        return testunit.getCaseSet().stream()
                .filter(testCase -> testCase.getId().equals(id))
                .findFirst()
                .orElse(null);
    }
}    
